package de.koller.worlders.engine.tiles;

import de.koller.oceanFramework.gl.util.ParticleTexture;
import de.koller.oceanFramework.gl.util.RasterManager;
import de.koller.oceanFramework.tree.Mother;
import de.koller.worlders.WSea;
import de.koller.worlders.engine.entity.IRenderable;
import de.koller.worlders.engine.entity.fog.Fog;
import de.koller.worlders.engine.tiles.data.TileBuffer;
import de.koller.worlders.engine.tiles.data.TileWrap;
import de.koller.worlders.render.VisualMap;
import de.koller.worlders.render.visual.DisplayRenderObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL20;

public class MapRendering extends WSea {
	
	public static final int SCREEN_TILES = 40;
	public static final int SCREEN_TILES_OFF = 10;
	
	@Mother private TileMap map;
	private boolean smoothLight = true;
	
	private ParticleTexture gridO;
	private ParticleTexture gridU;
//	private OceanShader gridShader;

//	private final Tile[][] tilebuffer = new Tile[ SCREEN_TILES ][ SCREEN_TILES ];
	private TileBuffer tileBuffer;
	private final Map< IRenderable, List< ITile > > renderTile = new HashMap<>();
	
	@Override
	protected void init() {
		super.init();
		
		tileBuffer = new TileBuffer( SCREEN_TILES, SCREEN_TILES, wroot.getTileMap().tileHolder );
		
		final int ts = 2048;
		ParticleTexture.MAG_FILTER = GL11.GL_LINEAR;
		ParticleTexture.MIN_FILTER = GL11.GL_LINEAR;
		gridO = ParticleTexture.generate( ts, ts );
		gridU = ParticleTexture.generate( ts, ts );
		RasterManager to = (RasterManager)VisualMap.gridO.getGenTexture();
		RasterManager tu = (RasterManager)VisualMap.gridU.getGenTexture();
		for( int x = 0; x < ts; x++ )
			for( int y = 0; y < ts; y++ ) {
				gridO.setPixel( to.colorAt( x%32, y%32 ), x, y );
				gridU.setPixel( tu.colorAt( x%32, y%32 ), x, y );
			}
		
		gridO.flush();
		gridU.flush();
//		gridShader = OceanShader.createShader( new GridShader() );
	}
	
	@Override
	protected void update( int elapsedTime ) {
		super.update( elapsedTime );
		
		if( wroot.getInput().isKeyPressed( Keyboard.KEY_L ) && !wroot.getGui().getConsoleInput().isOpen() )
			smoothLight = !smoothLight;
		
		int ox = -(int)Math.floor( map.getOffsetX() ) - SCREEN_TILES_OFF;
		int oy = -(int)Math.floor( map.getOffsetY() ) - SCREEN_TILES_OFF;
		tileBuffer.swapBuffer( ox, oy );
	}
	
	public void renderMap() {
		GL11.glEnable( GL11.GL_TEXTURE_2D );
		
		renderTile.clear();
		
		int ox = -(int)Math.floor( map.getOffsetX() ) - SCREEN_TILES_OFF;
		int oy = -(int)Math.floor( map.getOffsetY() ) - SCREEN_TILES_OFF;
		for( int x = ox; x < ox+SCREEN_TILES; x++ )
			for( int y = oy; y < oy+SCREEN_TILES; y++ ) {
				//Tile t = map.searchTile( x, y );
				//tilebuffer[x-ox][y-oy] = t;
				TileWrap t = tileBuffer.getTile( x, y );
				renderTile( t.getTile(), true );
			}
		
		renderRenderTiles();
	}
	
	private void renderRenderTiles() {
		for( IRenderable r : renderTile.keySet() ) {
			DisplayRenderObject dr = (DisplayRenderObject)r;
			dr.getGenTexture();
			dr.preRendering();
			
			for( ITile t : renderTile.get( r ) )
				dr.renderObject( t.getPosition().getXf(), t.getPosition().getYf(), 1, 1, t.getZ(), true );
		}
	}
	
	public void renderTile( Tile tile, float z, boolean buffered ) {
		if( tile == null )
			return;

		if( tile instanceof MOTile == false ) {
			bufferTileView( tile.getVisual(), tile, buffered );
			if( tile.getDeco() != null )
				bufferTileView( tile.getDeco().getVisual(), tile.getDeco(), buffered );
		} else {
			tile.getVisual().renderObject( tile.getPosition().getXf(), tile.getPosition().getYf(), 
					tile.getSize().getXf(), tile.getSize().getYf(), z, false );
		}
	}
	
	private void bufferTileView( IRenderable r, ITile t, boolean buffered ) {
		if( !r.isVisible() )
			return;
		if( !buffered ) {
			r.renderObject( t.getPosition().getXf(), t.getPosition().getYf(), 1, 1, t.getZ(), false );
		} else {
			if( renderTile.containsKey( r ) )
				renderTile.get( r ).add( t );
			else {
				List<ITile> c = new ArrayList<>();
				c.add( t );
				renderTile.put( r, c );
			}
		}
	}
	
	public void renderTile( Tile tile, boolean buffered ) {
		this.renderTile( tile, 0, buffered );
	}
	
	public void renderBackground() {
		wroot.background.renderBackground();
	}
	
	public void renderFog() {
		wroot.rendering.worlderV.setTransform();
		GL11.glEnable( GL11.GL_BLEND );
		GL11.glBlendFunc( GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA );
		GL11.glDisable( GL11.GL_TEXTURE_2D );
		
		renderTile.clear();
		
		int ox = -(int)Math.floor( map.getOffsetX() ) - SCREEN_TILES_OFF;
		int oy = -(int)Math.floor( map.getOffsetY() ) - SCREEN_TILES_OFF;
		for( int x = ox; x < ox+SCREEN_TILES; x++ )
			for( int y = oy; y < oy+SCREEN_TILES; y++ ) {
				//Tile t = tilebuffer[x-ox][y-oy];
				TileWrap t = tileBuffer.getTile( x, y );
				if( t == null )
					continue;
				Fog f = t.getTile().getFog();
				if( f == null )
					continue;
				bufferTileView( f.getVisual(), f, true );
			}
		
		renderRenderTiles();
		
		GL11.glDisable( GL11.GL_BLEND );
		GL11.glEnable( GL11.GL_TEXTURE_2D );
		wroot.rendering.worlderV.unsetTransform();
	}
	
	public void renderGrid() {
		wroot.rendering.worlderV.setTransform();
//		gridShader.useShader();
		
		GL11.glDisable( GL11.GL_ALPHA_TEST );
		//float x = ( map.getOffsetX() * WRendering.SCALE ) % WRendering.SCALE;
		//float y = ( map.getOffsetY() * WRendering.SCALE ) % WRendering.SCALE;
		//x = Math.round( x ) - WRendering.SCALE;
		//y = Math.round( y ) - WRendering.SCALE;
		
		float x = -map.getOffsetX() - 6;
		float y = -map.getOffsetY() - 6;
		x = Math.round( x );
		y = Math.round( y );
		
		GL11.glEnable( GL11.GL_BLEND );
		GL11.glColor3f( 1, 1, 1 );
		
		GL11.glBlendFunc( GL11.GL_ONE, GL11.GL_ONE );
		
//		GL14.glBlendEquation( GL14.GL_FUNC_ADD );
		GL20.glBlendEquationSeparate( GL14.GL_FUNC_ADD, GL14.GL_FUNC_ADD );
		gridO.draw2D( x, y, 64, 64, 0.05f );
		
//		GL14.glBlendEquation( GL14.GL_FUNC_REVERSE_SUBTRACT );
		GL20.glBlendEquationSeparate( GL14.GL_FUNC_REVERSE_SUBTRACT, GL14.GL_FUNC_ADD );
		gridU.draw2D( x, y, 64, 64, 0.06f );
		
		GL14.glBlendEquation( GL14.GL_FUNC_ADD );
		GL11.glDisable( GL11.GL_BLEND );
//		gridShader.unuseShader();
		
		GL11.glEnable( GL11.GL_ALPHA_TEST );
		
		wroot.rendering.worlderV.unsetTransform();
	}
	
//	private static class GridShader extends OceanShaderProgramS {
//
//		@Override
//		public String getVertexShader() {
//			return	"void main() {" +
//					"	gl_Position     = gl_ModelViewProjectionMatrix * gl_Vertex;" +
//					"	gl_TexCoord[0]  = gl_MultiTexCoord0;" +
//					"}";
//				
//		}
//		
//		@Override
//		public String getFragmentShader() {
//			return	"uniform sampler2D texSampler;" +
//					"" +
//					"void main() {" +
//					"	gl_FragColor = texture2D(texSampler, vec2(gl_TexCoord[0]));" +
//					"}";
//		}
//		
//	}

	public void renderFinalize() {		
		GL11.glStencilFunc( GL11.GL_EQUAL, 1, 1 );
		GL11.glStencilOp( GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP );
		
		this.renderGrid();
	
		GL11.glStencilFunc( GL11.GL_NOTEQUAL, 1, 1 );
		this.renderBackground();
			
		GL11.glDisable( GL11.GL_STENCIL_TEST );
		this.renderFog();
	
		GL11.glEnable( GL11.GL_STENCIL_TEST );
		GL11.glStencilFunc( GL11.GL_EQUAL, 1, 1 );
		map.getLight().renderLight();
	}
	
}
